/**
 * 网络消息包封装
 */

import NetProto from './NetProto'
import { NetCodeFuncMap } from './NetworkConstants';
import * as longlibs from "../libs/longlibs"

export var PackMsg = function (buffer): void {
  //MKLog.log("new pkg,buffer:",new Uint8Array(buffer));
  let ByteArray = Uint8Array;
  let that: any = {};
  let pos = 0, _buffer = buffer, _dv = new DataView(buffer);

  that.Data = function () {
    return _buffer;
  };

  that.readU32 = function () {
    //    let tmp = new ArrayBuffer(4);
    //    copyArray(tmp, 0, _buffer, pos, tmp.length);
    let ret = _dv.getUint32(pos, false);
    pos += 4;
    return ret;
  };

  that.readU16 = function () {
    let ret = _dv.getUint16(pos, false);
    pos += 2;
    return ret;
  };

  /**
   * 读 4*8字节
   */
  that.readS32 = function () {
    let ret = _dv.getUint32(pos, false);
    pos += 4;
    return ret;
  };

  that.readI64 = function () {
    let ret = _dv.getUint32(pos, false);
    pos += 4;

    let ret_2 = _dv.getUint32(pos, false);
    pos += 4;

    //处理高位低位
    let long = new longlibs(ret_2, ret);
    return long;
  }

  //todo:错误检查
  that.readBytes = function () {
    let length = that.readU16();
    //    let tmp = new ArrayBuffer(_buffer.length - pos);
    //    MKLog.log("readBytes", pos,new Uint8Array(_buffer));
    let newArray = new Uint8Array(length);
    copyArray(newArray, 0, new Uint8Array(_buffer), pos, newArray.length);
    pos += length;
    return newArray
  };

  that.readString = function () {
    let length = that.readU16();

    //let newArray = new Uint8Array(_buffer.byteLength - pos);
    let newArray = new Uint8Array(length);
    copyArray(newArray, 0, new Uint8Array(_buffer), pos, newArray.length);
    pos += length;
    let str = strdecode(newArray);
    return str;
  };

  //读取arraybuffer
  that.readBuffer = function () {
    let length = that.readU16();

    //截取到最后
    var newArrayBuffer = _buffer.slice(pos, pos + length);
    pos += length;
    return newArrayBuffer;
  }

  //读取布尔
  that.readBool = function () {
    let ret = _dv.getUint8(pos);
    pos += 1;
    return ret;
  }

  //encode
  //todo:暂只支持uint32,uint16,int32
  var writeNumber = function (nType, v) {
    //MKLog.log("write number:",nType,v);
    let vBuffer = null;
    if (nType === "uint32") {
      vBuffer = new ArrayBuffer(4);
      let view = new DataView(vBuffer);
      view.setUint32(0, v, false);

    } else if (nType === "uint16") {
      vBuffer = new ArrayBuffer(2);
      let view = new DataView(vBuffer);
      view.setUint16(0, v, false);
    } else if (nType === "int32") {
      vBuffer = new ArrayBuffer(4);
      let view = new DataView(vBuffer);
      view.setInt32(0, v, false);
    } else if (nType === "int64") {
      vBuffer = new ArrayBuffer(8);
      let view = new DataView(vBuffer);
      view.setInt32(4, v, false);
      view.setInt32(0, 0, false);
    }
    else {
      return
    }
    if (!vBuffer) {
      return
    }

    let newArray = new ByteArray(_buffer.byteLength + vBuffer.byteLength);
    copyArray(newArray, 0, new ByteArray(_buffer), 0, _buffer.byteLength);
    copyArray(newArray, _buffer.byteLength, new ByteArray(vBuffer), 0, vBuffer.byteLength);

    setUint8ArrayToBuffer(newArray);
  };

  /**
   * 写 4*8
   */
  that.writeU32 = function (v) {
    writeNumber("uint32", v);
  };

  that.writeU16 = function (v) {
    writeNumber("uint16", v);
  };

  that.writeS32 = function (v) {
    writeNumber("int32", v);
  };

  that.writeI64 = function (v) {
    writeNumber("int64", v);
  }

  that.writeString = function (str) {
    let bytes = strencode(str);
    that.writeU16(bytes.length);
    let newArray = new ByteArray(_buffer.byteLength + bytes.length);
    copyArray(newArray, 0, new ByteArray(_buffer), 0, _buffer.byteLength);
    copyArray(newArray, _buffer.byteLength, bytes, 0, bytes.length);

    setUint8ArrayToBuffer(newArray);
  };

  that.writeBytes = function (bytes) {
    that.writeU16(bytes.length);
    let newArray = new ByteArray(_buffer.byteLength + bytes.length);
    copyArray(newArray, 0, new Uint8Array(_buffer), 0, _buffer.byteLength);
    copyArray(newArray, _buffer.byteLength, bytes, 0, bytes.length);
    setUint8ArrayToBuffer(newArray);
  };

  that.writeBuffer = function (arrayBuffer) {
    that.writeU16(arrayBuffer.byteLength);

    let _bufferLen = _buffer.byteLength;
    let arrBufferLen = arrayBuffer.byteLength;

    var abAll = new ArrayBuffer(_bufferLen + arrBufferLen);

    var bodyView = new Int8Array(abAll, 0, abAll.byteLength);
    var _buffMsg = new Int8Array(_buffer);
    var arrMsg = new Int8Array(arrayBuffer);

    bodyView.set(_buffMsg);
    bodyView.set(arrMsg, _bufferLen);

    _buffer = abAll;
  }

  var strencode = function (str) {
    var byteArray = new ByteArray(str.length * 3);
    var offset = 0;
    for (var i = 0; i < str.length; i++) {
      var charCode = str.charCodeAt(i);
      var codes = null;
      if (charCode <= 0x7f) {
        codes = [charCode];
      } else if (charCode <= 0x7ff) {
        codes = [0xc0 | (charCode >> 6), 0x80 | (charCode & 0x3f)];
      } else {
        codes = [0xe0 | (charCode >> 12), 0x80 | ((charCode & 0xfc0) >> 6), 0x80 | (charCode & 0x3f)];
      }
      for (var j = 0; j < codes.length; j++) {
        byteArray[offset] = codes[j];
        ++offset;
      }
    }
    var _buffer = new ByteArray(offset);
    copyArray(_buffer, 0, byteArray, 0, offset);
    return _buffer;
  };

  var strdecode = function (buffer) {
    var bytes = new ByteArray(buffer);
    var array = [];
    var offset = 0;
    var charCode = 0;
    var end = bytes.length;
    let stringResult = "";
    while (offset < end) {
      if (bytes[offset] < 128) {
        charCode = bytes[offset];
        offset += 1;
      } else if (bytes[offset] < 224) {
        charCode = ((bytes[offset] & 0x3f) << 6) + (bytes[offset + 1] & 0x3f);
        offset += 2;
      } else {
        charCode = ((bytes[offset] & 0x0f) << 12) + ((bytes[offset + 1] & 0x3f) << 6) + (bytes[offset + 2] & 0x3f);
        offset += 3;
      }
      array.push(charCode);
      if (array.length > 10000) {
        stringResult += String.fromCharCode(...array);
        array = [];
      }
    }
    if (array.length < 10000) {
      stringResult += String.fromCharCode(...array);
    }
    return stringResult;
  };

  var copyArray = function (dest, doffset, src, soffset, length) {
    if ('function' === typeof src.copy) {
      // Buffer
      src.copy(dest, doffset, soffset, soffset + length);
    } else {
      // Uint8Array
      for (var index = 0; index < length; index++) {
        dest[doffset++] = src[soffset++];
      }
    }
  };

  var setUint8ArrayToBuffer = function (newArray) {
    let newBuffer = new ArrayBuffer(newArray.length);
    let dv = new Uint8Array(newBuffer);
    dv.set(newArray, 0);
    _buffer = newBuffer;
  };

  return that;
};

export default class MKPacket {
  public static CreatePackage = function (msgID) {
    //MKLog.log('[MKPacket] 发送和接收到的协议号 msgID =', msgID);
    NetCodeFuncMap;
    NetProto;
    var proto = NetProto[NetCodeFuncMap[msgID]]();
    proto.msgID = msgID;
    proto.F_msg_id = msgID;

    return proto;
  };

  public static PackBody = function (tbl) {
    let writer = new PackMsg(new ArrayBuffer(0));
    if (tbl && typeof (tbl.Pack) === 'function') {
      //MKLog.log('pack tbl');
      tbl.Pack(writer);
    }
    return writer.Data();
  };

  public static Unpack = function (data) {
    let reader = new PackMsg(data);

    let length = reader.readU16();
    let packageID = reader.readU32();
    let msgID = reader.readU16();

    let proto = this.CreatePackage(msgID);
    proto.Unpack(reader);
    proto.packageID = packageID;

    return proto;
  };
}
